#include "OpennavMap.h"
#include "OpennavUtils.h"
#include "BadaImage.h"

/*
 * OpennavMapSection
 */

OpennavMapSection::OpennavMapSection() : __pMap(null) {

}

OpennavMapSection::~OpennavMapSection() {
	if (__pMap != null) {
		delete __pMap;
		__pMap = null;
	}
}

result OpennavMapSection::Construct(Osp::Graphics::Canvas* map, opennav::Transformation& transformation, Osp::Locations::Coordinates& mapCenter) {
	__pMap = map;
	__transformation = transformation;
	__center = mapCenter;
	return E_SUCCESS;
}

void OpennavMapSection::Draw(Osp::Graphics::Canvas& canvas, const Osp::Graphics::Point& pan, float zoom) {
	Osp::Graphics::Rectangle srcBounds = (*__pMap).GetBounds();
	int drawWidth = srcBounds.width * zoom;
	int drawHeight = srcBounds.height * zoom;
	int cx = pan.x * zoom + canvas.GetBounds().width / 2, cy = pan.y * zoom + canvas.GetBounds().height / 2;
	Osp::Graphics::Rectangle destBounds = Osp::Graphics::Rectangle(cx - drawWidth / 2, cy - drawHeight / 2, drawWidth, drawHeight);
	canvas.Copy(destBounds, *__pMap, srcBounds);
}

const Osp::Locations::Coordinates& OpennavMapSection::GetCenter() const {
	return __center;
}

void OpennavMapSection::TransformCoordinatesToPixels(const Osp::Locations::Coordinates& coord, Osp::Graphics::Point& convertedPoint) const {
	opennav::Coordinate coordinate;
	OpennavUtils::Convert(coord, coordinate);
	opennav::Point point = __transformation.transform(coordinate);
	OpennavUtils::Convert(point, convertedPoint);
}

void OpennavMapSection::TransformPixelsToCoordinates(const Osp::Graphics::Point& point, Osp::Locations::Coordinates& convertedCoordinates) const {
	opennav::Point p;
	OpennavUtils::Convert(point, p);
	opennav::Coordinate coordinate = __transformation.inv_transform(p);
	OpennavUtils::Convert(coordinate, convertedCoordinates);
}

void OpennavMapSection::TransformCenter(const Osp::Graphics::Point& pan, float zoom, Osp::Locations::Coordinates& convertedMapCenter, float& zoomLevel) const {
	opennav::Point p;
	OpennavUtils::Convert(pan, p);
	p.x = -p.x + __pMap->GetBounds().width / 2;
	p.y = -p.y + __pMap->GetBounds().height / 2;
	opennav::Coordinate c = __transformation.inv_transform(p);
	OpennavUtils::Convert(c, convertedMapCenter);
	zoomLevel = __transformation.scale() / zoom;
}

/*
 * OpennavMap
 */

OpennavMap::OpennavMap() : __pMap(null), __pLock(null) { }

OpennavMap::~OpennavMap() {
	if (__pLock != null) {
		delete __pLock;
		__pLock = null;
	}
	if (__pMap != null) {
		delete __pMap;
		__pMap = null;
	}
}

result OpennavMap::Construct(opennav::Map* map) {
	__pLock = new Osp::Base::Runtime::Mutex();
	__pLock->Create();
	__pMap = map;

	return E_SUCCESS;
}

OpennavMapSection* OpennavMap::CreateSection(Osp::Locations::Coordinates& mapCenter, float zoomLevel, float azimuth, Osp::Graphics::Dimension& size) {
	__pLock->Acquire();
	opennav::Point screenCenter(size.width / 2, size.height / 2);
	opennav::Coordinate center;
	OpennavUtils::Convert(mapCenter, center);
	opennav::Transformation transformation;
	transformation.set_scale(zoomLevel);
	transformation.set_map_center(center);
	transformation.set_rotation(azimuth);
	transformation.set_screen_center(screenCenter);
	transformation.setup();
	opennav::Point p1(0, 0);
	opennav::Point p2(size.width - 1, size.height - 1);
	opennav::Point p3(0, size.height - 1);
	opennav::Point p4(size.width - 1, 0);
	opennav::Coordinate c1 = transformation.inv_transform(p1);
	opennav::Coordinate c2 = transformation.inv_transform(p2);
	opennav::Coordinate c3 = transformation.inv_transform(p3);
	opennav::Coordinate c4 = transformation.inv_transform(p4);
	opennav::Area bounds;
	bounds.add_to_bounds(c1);
	bounds.add_to_bounds(c2);
	bounds.add_to_bounds(c3);
	bounds.add_to_bounds(c4);
	int lod = 24 - (log2(zoomLevel) - 8);
	opennav::MapSelection* selection = new opennav::MapSelection();
	__pMap->create_selection(bounds, lod, *selection);
	Osp::Graphics::Canvas* result = new Osp::Graphics::Canvas();
	result->Construct(Osp::Graphics::Rectangle(0, 0, size.width, size.height));
	BadaImage renderTarget;
	renderTarget.Construct(result);
	opennav::MapRenderer renderer;
	renderer.render(*selection, transformation, renderTarget);
	delete selection;
	OpennavMapSection* section = new OpennavMapSection();
	section->Construct(result, transformation, mapCenter);
	__pLock->Release();
	return section;
}

void OpennavMap::FindCountry(Osp::Base::String& name, opennav::SearchCountryResult& result) {
	__pLock->Acquire();
	std::string convertedName;
	OpennavUtils::Convert(name, convertedName);
	__pMap->search_country(convertedName, result);
	__pLock->Release();
}

void OpennavMap::GetResult(int n, opennav::SearchCountryResult* result, opennav::Country& country) {
	__pLock->Acquire();
	__pMap->get_result(n, *result, country);
	__pLock->Release();
}

void OpennavMap::FindCity(Osp::Base::String& name, int countryId, opennav::SearchCityResult& result) {
	__pLock->Acquire();
	std::string convertedName;
	OpennavUtils::Convert(name, convertedName);
	__pMap->search_city(convertedName, countryId, result);
	__pLock->Release();
}

void OpennavMap::GetResult(int n, opennav::SearchCityResult* result, opennav::City& city) {
	__pLock->Acquire();
	__pMap->get_result(n, *result, city);
	__pLock->Release();
}

void OpennavMap::FindRoad(Osp::Base::String& name, int cityId, opennav::SearchRoadResult& result) {
	__pLock->Acquire();
	std::string convertedName;
	OpennavUtils::Convert(name, convertedName);
	__pMap->search_road(convertedName, cityId, result);
	__pLock->Release();
}

void OpennavMap::GetResult(int n, opennav::SearchRoadResult* result, opennav::Road& road) {
	__pLock->Acquire();
	__pMap->get_result(n, *result, road);
	__pLock->Release();
}

void OpennavMap::GetCountry(int countryId, opennav::Country& country) {
	__pLock->Acquire();
	__pMap->country(countryId, country);
	__pLock->Release();
}

void OpennavMap::GetCity(int cityId, opennav::City& city) {
	__pLock->Acquire();
	__pMap->city(cityId, city);
	__pLock->Release();
}

void OpennavMap::GetRegion(int regionId, opennav::Region& region) {
	__pLock->Acquire();
	__pMap->region(regionId, region);
	__pLock->Release();
}

bool OpennavMap::ReverseGeocode(Osp::Locations::Coordinates& coordinate, opennav::Road& road, opennav::RoadSegment& segment, opennav::RoadJunction& next_junction, Osp::Locations::Coordinates& c) {
	__pLock->Acquire();
	bool result = false;
	opennav::Coordinate coordin;
	OpennavUtils::Convert(coordinate, coordin);
	opennav::Coordinate coordout;
	result = __pMap->reverse_geocode(coordin, road, segment, next_junction, coordout);
	__pLock->Release();
	if (result) {
		OpennavUtils::Convert(coordout, c);
	}
	return result;
}

